package com.example.zegodemo;

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.projection.MediaProjection;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Surface;
import android.view.WindowManager;


import com.zego.zegoavkit2.screencapture.ve_gl.EglBase;
import com.zego.zegoavkit2.screencapture.ve_gl.EglBase14;
import com.zego.zegoavkit2.screencapture.ve_gl.GlRectDrawer;
import com.zego.zegoavkit2.screencapture.ve_gl.GlUtil;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import im.zego.zegoexpress.ZegoExpressEngine;
import im.zego.zegoexpress.callback.IZegoCustomVideoCaptureHandler;
import im.zego.zegoexpress.constants.ZegoPublishChannel;
import im.zego.zegoexpress.entity.ZegoVideoConfig;

/**
 * Copyright © 2016 Zego. All rights reserved.
 */
public
class VideoCaptureScreen extends IZegoCustomVideoCaptureHandler implements SurfaceTexture.OnFrameAvailableListener {

    private final String TAG = "VideoCaptureScreen：--";
    /**
     * 当裁剪宽高都等于 SIZE_FOR_NO_TAILOR 时，表示不做任何裁剪
     */
    public static final int SIZE_FOR_NO_TAILOR = 0;

    private HandlerThread mHandlerThread = null;
    private Handler mHandler = null;
    private final WindowManager mWindowManager;
    private Context mContext;
    private volatile MediaProjection mMediaProjection;
    private volatile VirtualDisplay mVirtualDisplay = null;
    private final DisplayMetrics mOutMetrics;
    private int mSdkSurfaceBufferWidth = 0;
    private int mSdkSurfaceBufferHeight = 0;
    private volatile boolean mIsCapturing = false;
    private volatile Surface mSurface = null;
    private volatile int mCaptureWidth;
    private volatile int mCaptureHeight;
    private final ZegoExpressEngine mZegoEngine;
    private SurfaceTexture mSysSurfaceTexture;
    private SurfaceTexture mSdkSurfaceTexture;
    private final float[] mTransformationMatrix = new float[]{
            1.0f, 0.0f, 0.0f, 0.0f,
            0.0f, 1.0f, 0.0f, 0.0f,
            0.0f, 0.0f, 1.0f, 0.0f,
            0.0f, 0.0f, 0.0f, 1.0f};
    private final float[] mInputMatrix = new float[16];
    private EglBase mEglSysToVideoMemory;
    private GlRectDrawer mSysToVideoMemoryDrawer;
    private EglBase mEglVideoMemoryToSDK;
    private GlRectDrawer mVideoMemoryToSDKDrawer;
    private int mFrameBufferId;
    private int mSysOesTextureId;
    private int mSys2DTextureId;
    private int mTailor2DTextureId;
    private int mTailorPositionX = 0;
    private int mTailorPositionY = 0;
    private int mTailorWidth;
    private int mTailorHeight;
    private boolean mEgl14Supported;
    private volatile int mDrawToSDKInterval = 1000 / 15;
    private final DrawRunnable mDrawRunnable = new DrawRunnable();

    private ScreenMetricsChangedListener mScreenMetricsChangedListener;

    /**
     * 构造方法
     *
     * @param context           上下文
     * @param mediaProjection   MediaProjection 可为空，但在开始采集后没有画面，
     *                          若在开始采集后才能获取到 MediaProjection，
     *                          可择机调用 setMediaProjection(MediaProjection mediaProjection) 方法获得画面
     * @param zegoExpressEngine ZegoExpress 引擎
     */
    public VideoCaptureScreen(Context context, MediaProjection mediaProjection, ZegoExpressEngine zegoExpressEngine) {
        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        mContext = context;
        mOutMetrics = new DisplayMetrics();
        getDisplayMetrics();
        mCaptureWidth = mOutMetrics.widthPixels;
        mCaptureHeight = mOutMetrics.heightPixels;

        mMediaProjection = mediaProjection;
        mZegoEngine = zegoExpressEngine;

        mTailorWidth = SIZE_FOR_NO_TAILOR;
        mTailorHeight = SIZE_FOR_NO_TAILOR;
    }

    /**
     * 设置屏幕裁剪区域的坐标与宽高，默认值为当前屏幕宽高
     * 注意事项：
     * 1. 当屏幕滑动或横竖屏切换后，需要更新裁剪区域，避免出现异常画面
     * 2. 不建议裁剪屏幕外区域，避免出现异常画面
     * <p>
     * 若裁剪区域为完整 View，可在 {@link android.app.Activity#onWindowFocusChanged(boolean hasFocus)} 方法中，
     * 通过 View 对象调用 {@link android.view.View#getLocationOnScreen(int[])} 获取坐标值
     * int[] position = new int[2];
     * ImageView.getLocationOnScreen(position);
     * System.out.println("getLocationOnScreen:" + "positionX：" + position[0] + "," + "positionY：" + position[1]);
     *
     * @param positionX    屏幕裁剪区域左上角坐标的 X 轴值，默认值为 0
     * @param positionY    屏幕裁剪区域左上角坐标的 Y 轴值，默认值为 0
     * @param tailorWidth  屏幕裁剪的宽，当为 SIZE_FOR_NO_TAILOR 时，positionX 和 positionY 将会失效，表示不做任何裁剪
     * @param tailorHeight 屏幕裁剪的高，当为 SIZE_FOR_NO_TAILOR 时，positionX 和 positionY 将会失效，表示不做任何裁剪
     */
    public void setTailorResolution(int positionX, int positionY, int tailorWidth, int tailorHeight) {
        if ((tailorWidth == SIZE_FOR_NO_TAILOR && tailorHeight == SIZE_FOR_NO_TAILOR) || (tailorWidth > 0 || tailorHeight > 0)) {
            mTailorPositionX = positionX;
            mTailorPositionY = positionY;
            mTailorWidth = tailorWidth;
            mTailorHeight = tailorHeight;
        }
    }

    /**
     * 设置外部采集后，开启采集前设置 MediaProjection
     * 若在开启采集前没有设置 MediaProjection 会没有画面
     * 但可在开启采集后择机设置 MediaProjection 会重新获得画面
     *
     * @param mediaProjection mediaProjection
     */
    public void setMediaProjection(MediaProjection mediaProjection) {
        Log.d(TAG, "-->:: setMediaProjection mediaProjection:" + mediaProjection + ", mMediaProjection: " + mMediaProjection);

        mMediaProjection = mediaProjection;

        if (mIsCapturing && mZegoEngine != null && mMediaProjection != null && mSurface != null) {
            if (mVirtualDisplay != null) {
                mVirtualDisplay.release();
            }
            mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture",
                    mCaptureWidth, mCaptureHeight, 1,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, mSurface, null, mHandler);
        }
    }

    /**
     * 设置帧率时，需同步设置该值
     *
     * @param fps 帧率
     */
    public void setFrameRate(int fps) {
        mDrawToSDKInterval = 1000 / fps;
    }

    public void setScreenMetricsChangedListener(ScreenMetricsChangedListener screenMetricsChangedListener) {
        mScreenMetricsChangedListener = screenMetricsChangedListener;
    }

    /**
     * 设置引擎外部采集提供的 SurfaceTexture 的 Buffer 默认大小
     * 设置编码分辨率的时候，需要同步设置该值
     *
     * @param width  宽
     * @param height 高
     */
    public void setResolution(final int width, final int height) {
        if (mHandler == null) {
            mSdkSurfaceBufferWidth = width;
            mSdkSurfaceBufferHeight = height;
            return;
        }

        mHandler.post(() -> {
            resizeSdkSurface(width, height);
        });
    }

    /**
     * SDK 通知将要开始采集视频帧，收到该回调后向 SDK 发送的视频帧数据才有效
     */
    @Override
    public void onStart(ZegoPublishChannel channel) {
        Log.i(TAG, " -->:: VideoCaptureScreen onStart callBack,channel:" + channel);
        mHandlerThread = new HandlerThread("ZegoScreenCapture");
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper());
        startCapture();
    }

    /**
     * SDK 通知将要停止采集视频帧
     */
    @Override
    public void onStop(ZegoPublishChannel channel) {
        Log.i(TAG, " VideoCaptureScreen onStop callBack,channel:" + channel);
        stopCapture();
        if (mHandlerThread != null) {
            mHandlerThread.quit();
            mHandlerThread = null;
        }
    }

    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        if (!mIsCapturing) {
            return;
        }
        drawOESTexture();
    }

    /**
     * 系统返回的内容在 mSysTextureId oes纹理上，需要将 oes 转换成 2D 纹理 mSDKTextureId（改纹理会根据帧率调度绘制给引擎）
     */
    private synchronized void drawOESTexture() {
        if (mEglSysToVideoMemory != null) {
            mEglSysToVideoMemory.makeCurrent();
        }

        if (mSysToVideoMemoryDrawer == null) {
            mSysToVideoMemoryDrawer = new GlRectDrawer();
        }

        mSysSurfaceTexture.updateTexImage();
        mSysSurfaceTexture.getTransformMatrix(mInputMatrix);

        if (mSys2DTextureId == 0) {
            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
            mSys2DTextureId = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D);
            GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mCaptureWidth, mCaptureHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
            mFrameBufferId = GlUtil.generateFrameBuffer(mSys2DTextureId);
        } else {
            GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBufferId);
        }

        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        mSysToVideoMemoryDrawer.drawOes(mSysOesTextureId, mInputMatrix,
                mCaptureWidth, mCaptureHeight, 0, 0, mCaptureWidth, mCaptureHeight);

        if (mTailor2DTextureId == 0) {
            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
            mTailor2DTextureId = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D);
        } else {
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTailor2DTextureId);
        }

        if (mTailorWidth == SIZE_FOR_NO_TAILOR && mTailorHeight == SIZE_FOR_NO_TAILOR) {
            GLES20.glCopyTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 0, 0, mCaptureWidth, mCaptureHeight, 0);
        } else {
            int positionY = mCaptureHeight - mTailorPositionY - mTailorHeight;
            GLES20.glCopyTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mTailorPositionX, positionY, mTailorWidth, mTailorHeight, 0);
        }

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
    }

    private void resizeSdkSurface(int width, int height) {
        if (mSdkSurfaceBufferWidth == width && mSdkSurfaceBufferHeight == height) {
            return;
        }
        mSdkSurfaceBufferWidth = width;
        mSdkSurfaceBufferHeight = height;

        if (mEglVideoMemoryToSDK != null && mEglVideoMemoryToSDK.hasSurface()) {
            mEglVideoMemoryToSDK.makeCurrent();

            if (mVideoMemoryToSDKDrawer != null) {
                mVideoMemoryToSDKDrawer.release();
                mVideoMemoryToSDKDrawer = null;
            }

            mEglVideoMemoryToSDK.releaseSurface();
            mEglVideoMemoryToSDK.detachCurrent();
        }

        if (mSdkSurfaceTexture != null) {
            mSdkSurfaceTexture.setDefaultBufferSize(mSdkSurfaceBufferWidth, mSdkSurfaceBufferHeight);
        }
    }

    private int startCapture() {
        if (mZegoEngine != null && !mIsCapturing) {
            mIsCapturing = true;
            initCaptureSize();
            getSurface();

            if (mMediaProjection != null) {
                mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture",
                        mCaptureWidth, mCaptureHeight, 1,
                        DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, mSurface, null, mHandler);
            }
        }
        return 0;
    }

    /**
     * 初始化采集分辨率，根据是否默认采集分辨率，来设置初始化值。
     */
    private void initCaptureSize() {
        mCaptureWidth = mOutMetrics.widthPixels;
        mCaptureHeight = mOutMetrics.heightPixels;

        if (mSdkSurfaceBufferWidth == 0 && mSdkSurfaceBufferHeight == 0) {
            // 默认 SDK 不会回调 setResolution，所以需要将其设置为默认值。除非外部主动设置，才使用外部设置的值。
            ZegoVideoConfig videoConfig = mZegoEngine.getVideoConfig();
            mSdkSurfaceBufferHeight = videoConfig.encodeHeight;
            mSdkSurfaceBufferWidth = videoConfig.encodeWidth;
        }
    }

    private void getSurface() {
        final CountDownLatch barrier = new CountDownLatch(1);
        if (mHandler != null) {
            mHandler.post(() -> {
                if (mEglSysToVideoMemory == null) {
                    mEglSysToVideoMemory = EglBase.create(null, EglBase.CONFIG_PIXEL_BUFFER);
                }

                if (!mEglSysToVideoMemory.hasSurface()) {
                    try {
                        mEglSysToVideoMemory.createDummyPbufferSurface();
                        mEglSysToVideoMemory.makeCurrent();
                        mSysToVideoMemoryDrawer = new GlRectDrawer();
                    } catch (RuntimeException e) {
                        // Clean up before rethrowing the exception.
                        mEglSysToVideoMemory.releaseSurface();
                        e.printStackTrace();
                        throw e;
                    }
                }
                mEgl14Supported = EglBase14.isEGL14Supported();
                mSysOesTextureId = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
                mSysSurfaceTexture = new SurfaceTexture(mSysOesTextureId);
                mSysSurfaceTexture.setDefaultBufferSize(mCaptureWidth, mCaptureHeight);
                mSurface = new Surface(mSysSurfaceTexture);
                mSysSurfaceTexture.setOnFrameAvailableListener(VideoCaptureScreen.this);

                if (mEglVideoMemoryToSDK == null) {
                    mEglVideoMemoryToSDK = EglBase.create(mEglSysToVideoMemory.getEglBaseContext(), EglBase.CONFIG_RECORDABLE);
                }

                if (!mEglVideoMemoryToSDK.hasSurface()) {
                    mSdkSurfaceTexture = mZegoEngine.getCustomVideoCaptureSurfaceTexture(ZegoPublishChannel.AUX);
                    mSdkSurfaceTexture.setDefaultBufferSize(mSdkSurfaceBufferWidth, mSdkSurfaceBufferHeight);
                    try {
                        mEglVideoMemoryToSDK.createSurface(mSdkSurfaceTexture);
                        mEglVideoMemoryToSDK.makeCurrent();
                        mVideoMemoryToSDKDrawer = new GlRectDrawer();
                    } catch (RuntimeException e) {
                        e.printStackTrace();
                        // Clean up before rethrowing the exception.
                        mEglVideoMemoryToSDK.releaseSurface();
                        return;
                    }
                }

                mHandler.postDelayed(mDrawRunnable, mDrawToSDKInterval);

                barrier.countDown();
            });
        }

        try {
            barrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private int stopCapture() {
        mIsCapturing = false;
        if (mVirtualDisplay != null) {
            mVirtualDisplay.release();
            mVirtualDisplay = null;
        }
        releaseEGLSurface();
        return 0;
    }

    private void releaseEGLSurface() {
        if (mHandler != null) {
            final CountDownLatch barrier = new CountDownLatch(1);

            mHandler.post(() -> {
                if (mHandler != null) {
                    mHandler.removeCallbacks(mDrawRunnable);
                    mHandler = null;
                }

                if (mEglVideoMemoryToSDK != null && mEglVideoMemoryToSDK.hasSurface()) {
                    mEglVideoMemoryToSDK.makeCurrent();

                    if (mVideoMemoryToSDKDrawer != null) {
                        mVideoMemoryToSDKDrawer.release();
                        mVideoMemoryToSDKDrawer = null;
                    }

                    mEglVideoMemoryToSDK.releaseSurface();
                    mEglVideoMemoryToSDK.detachCurrent();
                    mEglVideoMemoryToSDK.release();
                    mEglVideoMemoryToSDK = null;
                }

                if (mEglSysToVideoMemory != null && mEglSysToVideoMemory.hasSurface()) {
                    mEglSysToVideoMemory.makeCurrent();

                    if (mTailor2DTextureId != 0) {
                        int[] textures = new int[]{mTailor2DTextureId};
                        GLES20.glDeleteTextures(1, textures, 0);
                        mTailor2DTextureId = 0;
                    }

                    if (mFrameBufferId != 0) {
                        int[] frameBuffers = new int[]{mFrameBufferId};
                        GLES20.glDeleteFramebuffers(1, frameBuffers, 0);
                        mFrameBufferId = 0;
                    }

                    if (mSys2DTextureId != 0) {
                        int[] textures = new int[]{mSys2DTextureId};
                        GLES20.glDeleteTextures(1, textures, 0);
                        mSys2DTextureId = 0;
                    }

                    if (mSysOesTextureId != 0) {
                        int[] textures = new int[]{mSysOesTextureId};
                        GLES20.glDeleteTextures(1, textures, 0);
                        mSysOesTextureId = 0;
                    }

                    if (mSysToVideoMemoryDrawer != null) {
                        mSysToVideoMemoryDrawer.release();
                        mSysToVideoMemoryDrawer = null;
                    }

                    mEglSysToVideoMemory.releaseSurface();
                    mEglSysToVideoMemory.detachCurrent();
                    mEglSysToVideoMemory.release();
                    mEglSysToVideoMemory = null;
                }

                if (mSurface != null) {
                    mSurface.release();
                    mSurface = null;
                }

                barrier.countDown();
            });

            try {
                barrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private class DrawRunnable implements Runnable {
        @Override
        public void run() {
            // 每次都需要获取当前的屏幕分辨率（屏幕旋转了）
            resizeVirtualDisplayInNeed();
            drawRGBTextureToSDK();
            if (mHandler != null) {
                mHandler.postDelayed(mDrawRunnable, mDrawToSDKInterval);
            }
        }
    }

    public DisplayMetrics getDisplayMetrics() {
        int angle = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
        mWindowManager.getDefaultDisplay().getRealMetrics(mOutMetrics);
        DisplayMetrics metrics = new DisplayMetrics();
        // 当前应用旋转时，getRealMetrics 中返回的宽高是旋转后的宽高
        // 当退到后台，其他应用旋转时，getRealMetrics 中返回的宽高未经过旋转
        if ((angle == Surface.ROTATION_90 || angle == Surface.ROTATION_270) && (mOutMetrics.heightPixels > mOutMetrics.widthPixels)) {
            metrics.widthPixels = mOutMetrics.heightPixels;
            metrics.heightPixels = mOutMetrics.widthPixels;
        } else {
            metrics.widthPixels = mOutMetrics.widthPixels;
            metrics.heightPixels = mOutMetrics.heightPixels;
        }
        Log.d(TAG, "getDisplayMetrics() -->:: angle" + angle + "width: " + mOutMetrics.widthPixels + ", height: " + mOutMetrics.heightPixels + ", metrics Width: " + metrics.widthPixels + ", metrics Height: " + metrics.heightPixels);

        // 如果屏幕旋转 90° 或者 270° 是判断为横屏
        return metrics;
    }

    /**
     * resize VirtualDisplay，如果屏幕采集的宽高改变的话。
     */
    private void resizeVirtualDisplayInNeed() {
        DisplayMetrics metrics = getDisplayMetrics();

        int width = metrics.widthPixels;
        int height = metrics.heightPixels;

        Log.d(TAG, "resizeVirtualDisplayInNeed() -->:: width: " + width + ", height: " + height + ", mCaptureWidth: " + mCaptureWidth + ", mCaptureHeight: " + mCaptureHeight);

        // 由于是多线程操作，容易造成 mCaptureWidth == width，因为 width 是实时获取的而执行不到后面操作的现象
        // 导致拉流画面不同步，所以增加队列执行
        if (mCaptureWidth == width && mCaptureHeight == height) {
            return;
        }

        Log.d(TAG, "resizeVirtualDisplayInNeed() -->:: width: " + width + ", height: " + height + ", mCaptureWidth: " + mCaptureWidth + ", mCaptureHeight: " + mCaptureHeight);

        final ScreenMetricsChangedListener screenMetricsChangedListener = mScreenMetricsChangedListener;
        if (screenMetricsChangedListener != null) {
            screenMetricsChangedListener.onScreenMetricsChanged(width, height);
        }

        mCaptureWidth = width;
        mCaptureHeight = height;

        if (mEglSysToVideoMemory != null && mEglSysToVideoMemory.hasSurface()) {
            mEglSysToVideoMemory.makeCurrent();
        }

        if (mTailor2DTextureId != 0) {
            int[] textures = new int[]{mTailor2DTextureId};
            GLES20.glDeleteTextures(1, textures, 0);
            mTailor2DTextureId = 0;
        }

        if (mFrameBufferId != 0) {
            int[] frameBuffers = new int[]{mFrameBufferId};
            GLES20.glDeleteFramebuffers(1, frameBuffers, 0);
            mFrameBufferId = 0;
        }

        if (mSys2DTextureId != 0) {
            int[] textures = new int[]{mSys2DTextureId};
            GLES20.glDeleteTextures(1, textures, 0);
            mSys2DTextureId = 0;
        }

        if (mSysToVideoMemoryDrawer != null) {
            mSysToVideoMemoryDrawer.release();
            mSysToVideoMemoryDrawer = null;
        }

        if (mEglSysToVideoMemory != null) {
            mEglSysToVideoMemory.detachCurrent();
        }

        if (mSurface != null) {
            mSurface.release();
            mSurface = null;
        }

        if (mSysSurfaceTexture != null) {
            mSysSurfaceTexture.release();
            mSysSurfaceTexture = null;
        }

        if (mVirtualDisplay != null && mMediaProjection != null) {
            mVirtualDisplay.release();
            mSysSurfaceTexture = new SurfaceTexture(mSysOesTextureId);
            mSysSurfaceTexture.setDefaultBufferSize(mCaptureWidth, mCaptureHeight);
            mSurface = new Surface(mSysSurfaceTexture);
            mSysSurfaceTexture.setOnFrameAvailableListener(VideoCaptureScreen.this);

            mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture",
                    mCaptureWidth, mCaptureHeight, 1,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, mSurface, null, mHandler);
        }
    }

    /**
     * 将 拷贝的 2D纹理 mSDKTextureId 绘制到引擎
     */
    private synchronized void drawRGBTextureToSDK() {
        try {
            if (!mEglVideoMemoryToSDK.hasSurface() && mSdkSurfaceTexture != null) {
                mEglVideoMemoryToSDK.createSurface(mSdkSurfaceTexture);
            }

            mEglVideoMemoryToSDK.makeCurrent();

            if (mVideoMemoryToSDKDrawer == null) {
                mVideoMemoryToSDKDrawer = new GlRectDrawer();
            }

            long timestamp = TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime());
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

            // 纹理朝向问题。
            // 纹理需要在屏幕中显示为正朝向，那么纹理表示的时候就是需要倒着的。
            // OES 绘制成 2D 纹理，使用了 SurfaceTexture 提供的 TransformMatrix 「mSysSurfaceTexture.getTransformMatrix(mInputMatrix);」
            // 这个 TransformMatrix 的作用是使 OES 能正朝向的绘制到屏幕上。
            // 但是现在为了保证帧率，所以 OES 是绘制到帧缓存区上。因此帧缓冲区绑定的 2D 纹理也是倒着显示的。
            // 所以在采集区域的时候，需要以上下跌倒的形式去计算范围

            // mTailorTextureId 纹理大小就是裁剪的大小
            // 持续需要将该纹理绘制到 SDK 提供的画布上（画布的大小为编码分辨率，mSdkSurfaceBufferWidth， mSdkSurfaceBufferHeight）
            // 点进入 drawRgb，是可以看到第三个参数和第四个参数是没有作用的。不需要理会其作用
            // 由于客户需要的是黑边，那么我们只需要管理好 viewPort 即可，transformationMatrix 就可以忽略了。
            // 下面还是以上面指定裁剪的区域进行黑边处理

            int width, height;
            if (mTailorWidth == SIZE_FOR_NO_TAILOR || mTailorHeight == SIZE_FOR_NO_TAILOR) {
                width = mCaptureWidth;
                height = mCaptureHeight;
            } else {
                width = mTailorWidth;
                height = mTailorHeight;
            }

            // 作用是使图像正立显示
            int scaleWidth = mSdkSurfaceBufferWidth;
            int scaleHeight = mSdkSurfaceBufferHeight;
            if (mSdkSurfaceBufferHeight * width <= mSdkSurfaceBufferWidth * height) {
                scaleWidth = mSdkSurfaceBufferHeight * width / height; // 以高
            } else {
                scaleHeight = mSdkSurfaceBufferWidth * height / width;  // 以宽
            }

            Log.d(TAG, "-->:: width: " + width + ", height: " + height + ", scaleWidth: " + scaleWidth + ", scaleHeight: " + scaleHeight);
            Log.d(TAG, "-->:: mSdkSurfaceBufferWidth: " + mSdkSurfaceBufferWidth + ", mSdkSurfaceBufferHeight: " + mSdkSurfaceBufferHeight);
            Log.d(TAG, "-->:: mTailor2DTextureId: " + mTailor2DTextureId);

            mVideoMemoryToSDKDrawer.drawRgb(mTailor2DTextureId, mTransformationMatrix, mCaptureWidth, mCaptureHeight,
                    (mSdkSurfaceBufferWidth - scaleWidth) / 2, (mSdkSurfaceBufferHeight - scaleHeight) / 2,
                    scaleWidth, scaleHeight);
            if (mEgl14Supported) {
                ((EglBase14) mEglVideoMemoryToSDK).swapBuffers(timestamp);
            } else {
                mEglVideoMemoryToSDK.swapBuffers();
            }

            mEglVideoMemoryToSDK.detachCurrent();
        } catch (RuntimeException e) {
            e.printStackTrace();
        }
    }

    public interface ScreenMetricsChangedListener {
        void onScreenMetricsChanged(int width, int height);
    }
}
